package com.ruoyi.aitcommon.service;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.ruoyi.aitcommon.utils.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.aitcommon.anotation.AitFlow;
import com.ruoyi.aitcommon.config.ConfigManager;
import com.ruoyi.aitcommon.exception.AITServiceException;
import com.ruoyi.aitcommon.vo.FlowConfigVO;
import com.ruoyi.aitcommon.vo.FlowContextVO;
import com.ruoyi.aitcommon.vo.FlowStepVO;

import cn.hutool.core.util.StrUtil;

/**
 * FLow执行类
 * @author Administrator
 *
 */
@Service
public class FlowService {
	@Autowired
	ConfigManager configManager;
	@Autowired
	RuntimeManager runtimeManager;

//	举例:flowService.templateService('flow.test')
	@AitFlow("flow流程执行")
	public Object templateService(String flowCode) {
		return this.templateServiceEx(flowCode,null);
	}
//	举例:flowService.templateServiceP1('flow.test2',${data})
	@AitFlow("flow流程执行")
	public Object templateServiceP1(String flowCode,Object p1) {
		return this.templateServiceEx(flowCode,new Object[] {p1});
	}
//	举例:flowService.templateServiceP2('flow.test2',1234,${data})
	@AitFlow("flow流程执行")
	public Object templateServiceP2(String flowCode,Object p1,Object p2) {
		return this.templateServiceEx(flowCode,new Object[] {p1,p2});
	}

	/**
	 * flow核心方法,基于配置模板执行JSON代码
	 * 取消掉 synchronized,因为一个线程阻塞会导致所有线程阻塞
	 * @param flowCode flow模板路径,举例:flow.test
	 * @param args 可变参数,参数会放入flowdata中arg0,arg1...
	 * @return 流程简报 JSONObject对象
	 */
	@AitFlow("flow流程执行")
	public Object templateServiceEx(String flowCode, Object[] args){
		FlowConfigVO flowvo=null;
		Map<String, Object> flowData=null;
		try {
			if (StrUtil.isBlank(flowCode))
				throw new Exception("传入flowCode为空");
//			第一个参数是flow,获取flow json文件配置内容
			JSONObject flowInfo = (JSONObject) configManager.getContextByParm(flowCode);
			if (flowInfo == null)
				throw new Exception("获取流程配置失败,编码:" + flowCode);
//			转化flow json文件为强类型
			flowvo = flowInfo.toJavaObject(FlowConfigVO.class);
			
//			构造当前flow执行数据
//			flowData为局部变量,flow嵌套调用时,父flow数据会自动压入堆栈中
//			这里对params中使用到的${}进行翻译处理,确保每次动态翻译
			flowData = createFlowData(flowvo, args);
//			执行flow
//			这里对steps中method使用到的${}参数进行翻译
			Object ret=execFlow(flowCode, flowvo, flowData);

//			处理返回值
//			如果没有定义"flowret",返回上下文
//			因为切面会清除上下文,外部方法只能通过返回值拿到上下文
//			如果定义了"flowret",返回对应的上下文值
			if (StrUtil.isBlank(flowvo.flowret)) {
//				不能返回强类型,com.ruoyi.quartz.util.AbstractQuartzJob类[after]方法中拿不到AIT的VO类型
				Object retobj= JSONObject.toJSON(FlowUtils.getFlowContext());
				return retobj;
			} else {
//				如果是指定返回,AbstractQuartzJob无法记录FLow相关信息
				Object retobj = FlowParamUtils.getFlowParamsValue(flowvo.flowret, flowData);
				return retobj;
			}
		} catch (Exception ex) {
//			这里不能再抛出异常,否则整个进程会退出
			String msg="";
			boolean err=false;
			if(ex.getCause()!=null&&ex.getCause() instanceof InvocationTargetException) {
				InvocationTargetException ex1=(InvocationTargetException) ex.getCause();
				msg=ex.getMessage();
				if(ex1.getTargetException()!=null) {
					msg+=" 目标错误: "+ex1.getTargetException().getMessage();
				}
				err=true;
//				这里是服务切面抛出的异常
			} else if(ex.getCause()!=null&&ex.getCause() instanceof java.lang.IllegalArgumentException) {
//				这里是invoke参数类型或个数不匹配
				msg="调用服务参数类型或个数不匹配!"+ex.getMessage();
				err=true;
			} else {
//				服务切面中处理了服务调用异常,这里处理调用本身存在的异常
				msg=ex.getMessage();
				err=true;
			}
			if(err) {
				LogUtils.error("FlowService调用异常!Flow编码:[{}]", flowCode);
				LogUtils.error("错误消息:{}",msg);
				LogUtils.error("错误堆栈:",ex);
				FlowUtils.addBulletin("FlowService调用异常!");
				FlowUtils.addBulletin("Flow编码:[{}]",flowCode);
				FlowUtils.addBulletin("错误消息:{}",msg);
			}
//			FlowService为核心方法,不能抛错
			FlowUtils.getFlowContext().setRetCode(AITConst.ERRORCODE);
//			如果定义了"flowret",返回对应的上下文值
			if (StrUtil.isBlank(flowvo.flowret)) {
//				不能返回强类型,com.ruoyi.quartz.util.AbstractQuartzJob类[after]方法中拿不到AIT的VO类型
				Object retobj= JSONObject.toJSON(FlowUtils.getFlowContext());
				return retobj;
			} else {
				Object retobj = JSONResult.error(msg);
				return retobj;
			}

		}

	}

	/**
	 * 构造FLow运行数据
	 * 翻译Flow文件params中的${}参数
	 * @param flowvo
	 * @param args
	 * @return flowData Map
	 * @throws Exception
	 */
	private Map<String, Object> createFlowData(FlowConfigVO flowvo, Object[] args) throws Exception {
		Map<String, Object> flowData = new LinkedHashMap<String,Object>();
		
//		构造flowData,flow运行的所有数据均在flowData中
//		构造flowData,优先级: flow.params -> flow.extinfo -> args -> 配置文件 -> FlowContext
		
//		flowContext在切面中初始,这里获取并放入flowData
//		其中存放:flowID,flowName,startDate等,前缀 flowContext
		FlowContextVO flowContext = FlowUtils.getFlowContext();
		JSONObject flowjson = (JSONObject) JSONObject.toJSON(flowContext);
		flowData.put("flowContext", flowjson);

//		configManager加载了所有的文件配置,放入flowData
//		包含所有目录中的json,sql文件,前缀=目录
		flowData.putAll(configManager.getContext());

//		flow args参数:arg0,arg1....,放入flowData
//		无前缀
		Map<String, Object> argmap = createArgsMap(args);
		if (argmap != null)
			flowData.putAll(argmap);

//		flow.extinfo 该部分不做翻译,直接放入flowData
//		extinfo中的信息,无前缀
		if (flowvo.extinfo != null)
			flowData.putAll(flowvo.extinfo);

//		flow.params 上下文
//		翻译后放入flowData,所以${}中可以使用可以使用如下内容:
//		flowContext 上下文信息
//		configManager加载的所有json文件
//		args flow中传入参数
//		extinfo flow中的extinfo中的信息
//		params flow中的params的信息
//
//		params/extinfo/args 中的信息,无前缀,可以在step中使用
		if (flowvo.params != null) {
//			先放入params,然后再翻译.确保params可以引用到自身的常量
			flowData.putAll(flowvo.params);
			final Map<String, Object> transParams = FlowParamUtils.getFlowParamsValue(flowvo.params, flowData);
			flowData.putAll(transParams);
		}

		return flowData;
	}

	/**
	 * 执行flow steps
	 * 
	 * @param flowCode 流程编码
	 * @param flowVO   流程信息VO
	 * @param flowData 流程执行数据
	 * @return 最后一个step的返回Object
	 */
	private Object execFlow(String flowCode, FlowConfigVO flowVO, Map<String, Object> flowData) throws Exception {
		// 执行步骤
		LogUtils.info("流程日志:步骤执行开始====编码:[{}] 名称:[{}]", flowCode, flowVO.name);
		int stepcnt = 0;
		if(flowVO.steps==null)
			throw new Exception("flow中未定义steps");
		
		Object ret = null;
		for (FlowStepVO step : flowVO.steps) {
			stepcnt++;
			if (StringUtils.isNotBlank(step.name)) {
				LogUtils.info("流程日志:步骤{}====名称:{}", stepcnt, step.name);
				FlowUtils.addBulletin("步骤{}:{}", stepcnt, step.name);
			}
				
			
			// 执行方法
			if (StringUtils.isNotBlank(step.method))
				ret = BeanUtils.invokeMethod(step.method, flowData);
			else
				throw new Exception("flow中step.method执行方法为空!");
			
			// 处理返回
			if (StringUtils.isNotBlank(step.result) && ret != null) {
				String flowParam = JSONParamUtils.getParam(step.result);
				LogUtils.debug("流程日志:结果处理=====变量名称:{}", flowParam);
				MapUtils.putPathObject(flowParam, flowData, ret);
			}
		}
		LogUtils.info("流程日志:步骤执行结束====编码:[{}] 名称:[{}]", flowCode, flowVO.name);
		return ret;
	}

	/**
	 * 构造arg上下文:arg0,arg1...
	 * 
	 * @param args args对象数组
	 * @return args对象map
	 */
	private static Map<String, Object> createArgsMap(Object[] args) {
		Map<String, Object> retmap = new HashMap<String, Object>();

//		预制10个空arg,避免flow中获取arg报错
		for (int i = 0; i < 10; i++) {
			retmap.put("arg" + i, "");
		}

		if(args!=null&&args.length>0) {
//			设置有值的部分
			for (int i = 0; i < args.length; i++) {
				retmap.put("arg" + i, args[i]);
			}
		}

		return retmap;
	}

}
